using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;

// ReSharper disable InconsistentNaming
// ReSharper disable UnusedMember.Local
// ReSharper disable MemberCanBePrivate.Local
// ReSharper disable FieldCanBeMadeReadOnly.Local

namespace Pixeldyne.Core.Network
{
	public static class DnsTools
	{
		public static IList<string> Lookup(string domain)
		{
			var ipAddresses = new List<string>();

			IPHostEntry ipHost = Dns.GetHostEntry(domain);

			IPAddress[] addr = ipHost.AddressList;

			if (addr.Length > 0)
			{
				ipAddresses.Add(addr[0].ToString());
			}

			return ipAddresses;
		}

		public static IDictionary<string, int> GetMailRecords(string domain)
		{
			if (Environment.OSVersion.Platform != PlatformID.Win32NT)
			{
				throw new NotSupportedException("The dnsapi.dll is not available on your platform.");
			}

			var records = new Dictionary<string, int>();

			var queryResults = IntPtr.Zero;

			int errorNumber = DnsQuery(ref domain, QueryTypes.DNS_TYPE_MX, QueryOptions.DNS_QUERY_BYPASS_CACHE,
			                           0, ref queryResults, 0);

			if (errorNumber != 0)
			{
				throw new Win32Exception(errorNumber);
			}

			DnsRecords.MXRecord mxRecord;

			for (IntPtr iterator = queryResults; !iterator.Equals(IntPtr.Zero); iterator = mxRecord.pNext)
			{
				mxRecord = (DnsRecords.MXRecord)Marshal.PtrToStructure(iterator, typeof(DnsRecords.MXRecord));

				if (mxRecord.wType != (short)QueryTypes.DNS_TYPE_MX) continue;

				string mxDomain = Marshal.PtrToStringAuto(mxRecord.pNameExchange);

				if (mxDomain == null || string.IsNullOrWhiteSpace(mxDomain) || records.ContainsKey(mxDomain)) continue;

				records.Add(mxDomain, mxRecord.wPreference);
			}

			DnsRecordListFree(queryResults, 0);

			var sortedRecords = from record in records
			                    orderby record.Value ascending
			                    select record;

			return sortedRecords.ToDictionary(dict => dict.Key, dict => dict.Value);
		}


		[DllImport("dnsapi", EntryPoint = "DnsQuery_W", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
		private static extern int DnsQuery([MarshalAs(UnmanagedType.VBByRefStr)] ref string pszName,
		                                   QueryTypes wType, QueryOptions options, int aipServers, ref IntPtr ppQueryResults, int pReserved);

		[DllImport("dnsapi", CharSet = CharSet.Auto, SetLastError = true)]

		private static extern void DnsRecordListFree(IntPtr pRecordList, int FreeType);


		private enum QueryOptions
		{

			DNS_QUERY_ACCEPT_TRUNCATED_RESPONSE = 1,

			DNS_QUERY_BYPASS_CACHE = 8,
			DNS_QUERY_DONT_RESET_TTL_VALUES = 0x100000,
			DNS_QUERY_NO_HOSTS_FILE = 0x40,
			DNS_QUERY_NO_LOCAL_NAME = 0x20,
			DNS_QUERY_NO_NETBT = 0x80,
			DNS_QUERY_NO_RECURSION = 4,
			DNS_QUERY_NO_WIRE_QUERY = 0x10,
			DNS_QUERY_RESERVED = -16777216,
			DNS_QUERY_RETURN_MESSAGE = 0x200,
			DNS_QUERY_STANDARD = 0,
			DNS_QUERY_TREAT_AS_FQDN = 0x1000,
			DNS_QUERY_USE_TCP_ONLY = 2,
			DNS_QUERY_WIRE_ONLY = 0x100
		}

		private enum QueryTypes : short
		{
			DNS_TYPE_A = 1,
			DNS_TYPE_NS = 2,
			DNS_TYPE_CNAME = 5,
			DNS_TYPE_SOA = 6,
			DNS_TYPE_PTR = 12,
			DNS_TYPE_HINFO = 13,
			DNS_TYPE_MX = 15,
			DNS_TYPE_TXT = 16,
			DNS_TYPE_AAAA = 28
		}

		private static class DnsRecords
		{
			[StructLayout(LayoutKind.Sequential)]
			public struct MXRecord
			{
				public IntPtr pNext;
				public string pName;
				public short wType;
				public short wDataLength;
				public int flags;
				public int dwTtl;
				public int dwReserved;
				public IntPtr pNameExchange;
				public short wPreference;
				public short Pad;
			}
		}
	}
}